import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:kq_flutter_core_widget/widgets/keepalive/kq_keep_alive.dart';

/// 用于嵌套在系统Page界面的导航
class NavigationPageStack extends StatefulWidget {
  const NavigationPageStack({
    super.key,
    this.controller,
    this.drawerGlobalKey,
    this.childWidth,
    this.childHeight
  });

  final NavigationPageStackController? controller;

  /// 当此界面创建在抽屉之上时赋值
  final GlobalKey<ScaffoldState>? drawerGlobalKey;

  /// 指定子界面的宽度，不指定则子界面最大充满父类宽度
  final double? childWidth;

  /// 指定子界面的高度，不指定则子界面最大充满父类高度
  final double? childHeight;

  @override
  State<NavigationPageStack> createState() => NavigationPageState();
}

class NavigationPageState extends State<NavigationPageStack> {
  late PageController _pageController;

  @override
  void initState() {
    widget.controller?._attachState(this);
    _pageController = PageController(
        initialPage: max(0, (widget.controller?._routes.length ?? 0) - 1));
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    widget.controller?._stateDispose();
  }

  @override
  void didUpdateWidget(covariant NavigationPageStack oldWidget) {
    super.didUpdateWidget(oldWidget);
    var lastPage = max(0, (widget.controller?._routes.length ?? 0) - 1);
    if (oldWidget.controller != widget.controller) {
      oldWidget.controller?._stateDispose();
      widget.controller?._attachState(this);
      _pageController = PageController(initialPage: lastPage);
    } else if (_currentPage != lastPage) {
      _pageController = PageController(initialPage: lastPage);
    }
  }

  double? get _currentPage {
    var count = _pageController.positions;
    if (count.isEmpty || count.length != 1) {
      return null;
    }
    return _pageController.page;
  }

  /// 跳转下一个界面
  _goNext() {
    if (!mounted) {
      return;
    }

    setState(() {
      _open();
    });
  }

  void _open() async {
    if (!mounted) {
      return;
    }

    var routes = widget.controller?._routes;
    if (routes == null || routes.isEmpty) {
      return;
    }

    if (_currentPage == null) {
      return;
    }

    var length = routes.length;
    var route = routes.last;
    if (route._page.widthAnim && length > 1) {
      await _pageController.animateToPage(length - 1,
          duration: const Duration(milliseconds: 200),
          curve: Curves.fastOutSlowIn);
    } else {
      _pageController.jumpToPage(length - 1);
    }

    if (_currentPage == routes.length - 1) {
      return;
    }

    _open();
  }

  /// 关闭栈顶的界面，如果[couldBack]为false时则不执行
  Future _pop() async {
    if (!mounted) {
      return;
    }

    var routes = widget.controller?._routes;
    if (routes == null || routes.isEmpty) {
      return;
    }

    var page = _currentPage;
    if (page == null) {
      return;
    }

    var newPageIndex = max(0, routes.length - 2);
    if (newPageIndex == page) {
      return;
    }

    await _pageController.animateToPage(newPageIndex,
        duration: const Duration(milliseconds: 200),
        curve: Curves.fastOutSlowIn);
  }

  /// 弹出栈中所有界面
  void _popAll() {
    if (mounted) {
      setState(() {});
    }

    if (widget.controller?.couldBack != true) {
      _dragCloseDrawer();
    }
  }

  @override
  Widget build(BuildContext context) {
    Widget result = PageView.builder(
      scrollBehavior: const ScrollBehavior().copyWith(overscroll: false),
      controller: _pageController,
      scrollDirection: Axis.vertical,
      physics: const NeverScrollableScrollPhysics(),
      // 不能指定页面总长度，否则itemBuilder会重复创建同一个界面多次，这会触发EasyRefresh一个Bug：https://github.com/xuelongqy/flutter_easy_refresh/issues/782
      // itemCount: widget.controller?._routes.length ?? 0,
      itemBuilder: (context, index) {
        try {
          /// 存在移除的过程界面发生重绘
          var routes = widget.controller?._routes;
          if (routes == null ||
              routes.isEmpty ||
              index < 0 ||
              index >= routes.length) {
            return null;
          }
          return _buildItem(routes[index]._page);
        } catch (e) {
          return null;
        }
      },
    );

    result = _ShareDataWidget(controller: widget.controller, child: result);
    return result;
  }

  Widget _buildItem(NavigationPage page) {
    Widget result = KqKeepAlive(child: page.widgetBuilder.call(this));
    if (widget.childWidth != null || widget.childHeight != null) {
      result = SizedBox(
        width: widget.childWidth,
        height: widget.childHeight,
        child: result,
      );
    }

    if (widget.drawerGlobalKey != null) {
      result = GestureDetector(
        onTap: () {
          // 捕获点击,防止child不处理点击触发关闭抽屉点击事件
        },
        child: result,
      );
    }

    result = Container(
      width: double.infinity,
      alignment: Alignment.topRight,
      child: result,
    );

    if (widget.drawerGlobalKey == null) {
      return result;
    }

    var dragCloseDrawer = page.dragCloseDrawer;
    result = GestureDetector(
      behavior: HitTestBehavior.opaque,
      onHorizontalDragUpdate: !dragCloseDrawer ? (details) {} : null,
      onTap: dragCloseDrawer ? _dragCloseDrawer : null,
      child: result,
    );

    return result;
  }

  void _dragCloseDrawer() {
    var state = widget.drawerGlobalKey?.currentState;
    if (state == null || !state.mounted) {
      return;
    }
    state.closeEndDrawer();
    state.closeDrawer();
  }

  /// 刷新界面数据
  void update([VoidCallback? fn]) {
    if (!mounted) {
      return;
    }
    setState(fn ?? () {});
  }
}

class _ShareDataWidget extends InheritedWidget {
  const _ShareDataWidget({
    Key? key,
    this.controller,
    required Widget child,
  }) : super(key: key, child: child);

  final NavigationPageStackController? controller; //需要在子树中共享的数据，保存点击次数

  /// 该回调决定当data发生变化时，是否通知子树中依赖data的Widget重新build
  @override
  bool updateShouldNotify(_ShareDataWidget oldWidget) {
    return oldWidget.controller != controller;
  }
}

/// 导航页面
///
/// [id]用来唯一标识当前界面，如果需要使用[NavigationPageStackController] .popUntil .isTopById方法，则需要指定此值且保证每个界面id唯一。
/// 如果跳转界面时需要伴随动画，则需要将[widthAnim]置为true。[widgetBuilder]用于创建当前界面需要显示的内容。
/// 如果[NavigationPageStack]是抽屉的子界面，则[dragCloseDrawer]可以控制是否允许侧滑或点击空白区域关闭抽屉。
class NavigationPage {
  NavigationPage(
      {this.id = '',
      this.title,
      this.widthAnim = true,
      this.dragCloseDrawer = false,
      this.params,
      required this.widgetBuilder});

  /// 界面唯一标识，用来判断当前界面是否发生变更
  final String id;

  /// 用作界面标题
  final String? title;

  /// 需要显示的界面内容
  final Widget Function(NavigationPageState state) widgetBuilder;

  /// 界面进入是否执行动画
  final bool widthAnim;

  /// 是否可以侧滑或点击空白区域关闭抽屉，仅当[NavigationPageStack.drawerGlobalKey]被赋值时有效
  final bool dragCloseDrawer;

  /// 当前界面参数
  dynamic params;
}

/// 页面路由器
class Route<T> {
  Route(this._page);

  NavigationPage _page;
  final Completer<T?> _completer = Completer();

  Future<T?> get future => _completer.future;

  NavigationPage get page => _page;
}

/// [NavigationPageStack]控制器
class NavigationPageStackController {
  NavigationPageStackController({NavigationPage? initPage, this.id}) {
    if (initPage != null) {
      _routes.add(Route(initPage));
    }
  }

  static final Map<String, NavigationPageStackController> _map = {};

  /// 控制器id，请保证唯一，用于通过[get]全局获取控制器
  final String? id;

  final List<Route> _routes = [];
  NavigationPageState? _state;
  final List<RouteListener> _listeners = [];

  _addToMap() {
    if (id == null || id!.isEmpty) {
      return;
    }

    _map.putIfAbsent(id!, () => this);
  }

  _removeFromMap() {
    if (id == null || id!.isEmpty) {
      return;
    }
    _map.remove(id!);
  }

  void _attachState(NavigationPageState state) {
    _state = state;
    _addToMap();
  }

  void _stateDispose() {
    _state = null;
    _removeFromMap();
    _clearRoute();
  }

  bool _clearRoute([String? untilPageId]) {
    int removeCount = 0;
    var routes = [..._routes];
    for (int i = routes.length - 1; i >= 0; i--) {
      var route = routes[i];
      if (untilPageId == null || untilPageId != route._page.id) {
        route._completer.complete(null);
        var pre = i == 0 ? null : routes[i - 1];
        _notifyRoutePop(route, pre);
        _routes.remove(route);
        removeCount++;
      }
    }

    return removeCount > 0;
  }

  /// 跳转下一个界面
  Future<T?> goNext<T extends Object?>(NavigationPage page) {
    var route = Route<T>(page);
    var current = this.current;
    _routes.add(route);
    _state?._goNext();
    _notifyRoutePush(current, route);
    return route.future;
  }

  /// 将当前界面替换为新界面
  Future<T?> replace<T extends Object?>(NavigationPage page) {
    Route<T> route;
    if (_routes.isNotEmpty) {
      route = _routes[_routes.length - 1] as Route<T>;
      var oldPage = route.page;
      route._page = page;
      _state?.update();
      _notifyRouteReplace(Route(oldPage), route);
    } else {
      route = Route<T>(page);
      _routes.add(route);
      _state?._goNext();
      _notifyRouteReplace(null, route);
    }
    return route.future;
  }

  /// 关闭当前界面跳转下一个界面
  Future<T?> popGoNext<T extends Object?>(NavigationPage page,
      {dynamic result}) async {
    await pop(result: result);
    return goNext(page);
  }

  /// 关闭所有界面然后打开新的界面
  Future<T?> popAllGoNext<T>(NavigationPage page) {
    _clearRoute();
    return goNext(page);
  }

  /// 是否可以回退
  bool get couldBack => _routes.isNotEmpty;

  /// 判断指定[page]是否在栈顶
  bool isTop(NavigationPage page) {
    if (!couldBack) {
      return false;
    }

    return _routes[_routes.length - 1]._page == page;
  }

  /// 判断指定[pageId]界面是否在栈顶
  bool isTopById(String pageId) {
    if (!couldBack) {
      return false;
    }

    return _routes[_routes.length - 1]._page.id == pageId;
  }

  /// 关闭栈顶的界面
  Future pop({dynamic result}) async {
    if (!couldBack) {
      return;
    }

    var state = _state;
    if (state != null) {
      await state._pop();
    }
    var route = _routes.last;
    _routes.remove(route);
    route._completer.complete(result);
    state?.update();
    _notifyRoutePop(route, current);
    if (!couldBack) {
      // 关闭最后一个界面
      _state?._dragCloseDrawer();
    }
  }

  /// 关闭所有界面直到指定页面Id
  void popUntil<T>(String pageId) {
    if (_clearRoute(pageId)) {
      _state?._popAll();
    }
  }

  /// 弹出栈中所有界面
  void popAll() {
    if (_clearRoute()) {
      _state?._popAll();
    }
  }

  List<Route> get routes => [..._routes];

  /// 栈顶路由
  Route? get current {
    if (_routes.isEmpty) {
      return null;
    }
    return _routes.last;
  }

  /// 获取当前界面参数
  dynamic get params {
    if (_routes.isEmpty) {
      return null;
    }

    return _routes.last.page.params;
  }

  /// 根据key获取当前界面参数，需要当前界面参数类型为map
  S? getArg<S>(String key) {
    var arg = params;
    if (arg == null) {
      return null;
    }

    if (arg is! Map) {
      return null;
    }

    return arg[key];
  }

  void addRouteListener(RouteListener listener) {
    _listeners.add(listener);
  }

  void removeRouteListener(RouteListener listener) {
    _listeners.remove(listener);
  }

  void _notifyRoutePush(Route? current, Route next) {
    for (var element in _listeners) {
      element._onPush(current, next);
      element._onChange();
    }
  }

  void _notifyRoutePop(Route current, Route? pre) {
    for (var element in _listeners) {
      element._onPop(current, pre);
      element._onChange();
    }
  }

  void _notifyRouteReplace(Route? oldRoute, Route newRoute) {
    for (var element in _listeners) {
      element._onReplace(oldRoute, newRoute);
      element._onChange();
    }
  }

  /// 根据id全局查找控制器
  static NavigationPageStackController? get(String id) => _map[id];

  /// 根据上下文，获取最近一个父组件提供的控制器。如果需要夸层级获取，可使用[get]方法
  static NavigationPageStackController of(BuildContext context) {
    var result = maybeOf(context);
    assert(() {
      if (result == null) {
        final bool hiddenByBoundary = LookupBoundary
            .debugIsHidingAncestorWidgetOfExactType<_ShareDataWidget>(context);
        final List<DiagnosticsNode> information = <DiagnosticsNode>[
          if (hiddenByBoundary) ...<DiagnosticsNode>[
            ErrorSummary(
                'NavigationPageStackController.of() was called with a context that does not have access to a NavigationPageStack widget.'),
            ErrorDescription(
                'The context provided to NavigationPageStackController.of() does have a NavigationPageStack widget ancestor, but it is hidden by a LookupBoundary.'),
          ] else ...<DiagnosticsNode>[
            ErrorSummary(
                'NavigationPageStackController.of() was called with a context that does not contain a NavigationPageStack widget.'),
            ErrorDescription(
                'No NavigationPageStack widget ancestor could be found starting from the context that was passed to NavigationPageStackController.of().'),
          ],
          ErrorDescription(
            'The context used was:\n  $context',
          ),
          ErrorHint(
              'This usually means that the provided context is not associated with a NavigationPageStack.'),
        ];
        throw FlutterError.fromParts(information);
      }
      return true;
    }());
    return result!;
  }

  /// 根据上下文，获取最近一个父组件提供的控制器。如果需要夸层级获取，可使用[get]方法
  static NavigationPageStackController? maybeOf(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<_ShareDataWidget>()
        ?.controller;
  }
}

/// 路由监听
class RouteListener {
  RouteListener({this.onPush, this.onPop, this.onReplace, this.onChange});

  /// 打开新界面
  ///
  /// [current]为当前界面，如果此值为null，则代表第一次打开界面。[next]为即将要打开的界面。
  final Function(Route? current, Route next)? onPush;

  /// 关闭当前界面
  ///
  /// [current]为当前即将关闭的界面。[pre]为关闭当前界面后显示的界面，如果此值为null，则代表所有界面被关闭。
  final Function(Route current, Route? pre)? onPop;

  /// 当前界面替换新界面
  ///
  /// [oldRoute]被替换的界面，如果此值为null，则代表之前不存在任何界面。[newRoute]为新的需要展示的界面。
  final Function(Route? oldRoute, Route newRoute)? onReplace;

  /// 界面打开或关闭时的回调
  final Function()? onChange;

  void _onPush(Route? current, Route next) {
    onPush?.call(current, next);
  }

  void _onPop(Route current, Route? pre) {
    onPop?.call(current, pre);
  }

  void _onReplace(Route? oldRoute, Route newRoute) {
    onReplace?.call(oldRoute, newRoute);
  }

  void _onChange() {
    onChange?.call();
  }
}
